﻿using System;
using System.Collections.Generic;
using System.Text;
using NGinnBPM.Lib.Schema;
using NGinnBPM.Lib.Data;
using NGinnBPM.Lib.Interfaces;
using NLog;
using System.IO;
using System.Diagnostics;

namespace NGinnBPM.Lib.Services
{
    /// <summary>
    /// Process package store.
    /// Handles loading package files and process definitions.
    /// </summary>
    public class XmlProcessPackageStore : IProcessPackageStore
    {
        private Logger log = LogManager.GetCurrentClassLogger();
        private string _pkgFile;
        private XmlPackageInfo _xpi = null;
        private PackageDefinition _package = null;
        private XmlProcessDefinitionBuilder _bld = new XmlProcessDefinitionBuilder();

        private class ProcessInfo
        {
            public ProcessDefinition ProcessDef;
            public string ProcessFileName;
            public DateTime LoadedDate;
        }

        private Dictionary<string, ProcessInfo> _processes = new Dictionary<string, ProcessInfo>();


        public XmlProcessPackageStore()
        {
        }

        public XmlProcessPackageStore(string packageFile)
        {
            _pkgFile = packageFile;
        }

        /// <summary>
        /// Name of package definition XML file
        /// </summary>
        public string PackageFile
        {
            get { return _pkgFile; }
            set { _pkgFile = value; }
        }

        public string BaseDirectory
        {
            get { return Path.GetDirectoryName(_pkgFile); }
        }

        public void ReloadPackage()
        {
            XmlPackageInfo xpi = new XmlPackageInfo();
            xpi.Load(_pkgFile);
            _xpi = xpi;
            PackageDefinition pd = new PackageDefinition();
            pd.Name = _xpi.Name;
            _package = pd;
            log.Debug("Loaded package information: {0}", _xpi.Name);

            foreach (string schemaFile in _xpi.SchemaFiles)
            {
                log.Info("Loading package {0} data schema from {1}", pd.Name, schemaFile);
                LoadSchema(schemaFile);
            }

            foreach (string processFile in _xpi.ProcessFiles)
            {
                log.Info("Loading process definition from {0}.{1}", _xpi.Name, processFile);
                LoadProcessFile(processFile);
            }
            foreach (string k in xpi.Aliases.Keys)
                _package.SetAlias(xpi.Aliases[k], k);
        }

        private void LoadSchema(string schemaFile)
        {
            string baseDir = Path.Combine(BaseDirectory, schemaFile);
            TypeSet ts = _bld.LoadTypeSet(baseDir);
            _package.PackageTypes.Add(ts);
        }

        private void LoadProcessFile(string fileName)
        {
            try
            {
                string file = Path.Combine(BaseDirectory, fileName);
                ProcessDefinition pd = _bld.LoadProcessDefinition(file, _package);
                List<ValidationMessage> msgs = new List<ValidationMessage>();
                bool b = pd.Validate(msgs);
                string defid = string.Format("{0}.{1}", _package.Name, pd.LocalName);
                log.Log(b ? LogLevel.Info : LogLevel.Warn, "Validation of process {0} result: {1}. {2} messages.", defid, b, msgs.Count);
                foreach (ValidationMessage msg in msgs)
                {
                    log.Log(msg.IsError ? LogLevel.Warn : LogLevel.Info, "Validation of {0}: {1}", defid, msg.ToString());
                }
                _package.ProcessDefinitionNames.Add(pd.LocalName);
                _package.ProcessDefinitions.Add(pd);
                log.Info("Process loaded: {0}.{1}", _package.Name, pd.DefinitionId);
                if (this.ProcessReload != null)
                {
                    ProcessReload(pd);
                }
            }
            catch (Exception ex)
            {
                log.Warn("Failed to load process definition: {0}", fileName);
                throw;
            }
        }

        public void ReloadProcess(string processName)
        {

        }


        #region IProcessPackageStore Members

        public PackageDefinition GetPackageDefinition()
        {
            if (_package != null) return _package;
            lock (this)
            {
                if (_package != null) return _package;
                ReloadPackage();
                return _package;
            }
            return _package;
        }

        

        public ProcessDefinition GetProcessDefinition(string name)
        {
            PackageDefinition pd = GetPackageDefinition();
            string ali = name;
            if (pd.Aliases.ContainsKey(name)) ali = pd.Aliases[name];
            return pd.ProcessDefinitions.Find(x => x.LocalName == ali);
        }

        public ProcessDefinition GetProcessDefinition(string name, int version)
        {
            return GetProcessDefinition(string.Format("{0}.{1}", name, version));
        }

        public Stream GetPackageContentStream(string contentName)
        {
            string fp = Path.Combine(BaseDirectory, contentName);
            return File.Open(fp, FileMode.Open, FileAccess.Read);
        }

        public Stream GetPackageDefinitionStream()
        {
            Stream stm = File.Open(PackageFile, FileMode.Open, FileAccess.Read);
            return stm;
        }

        #endregion

        public void DetectModificationsAndReload()
        {

        }

        public delegate void ProcessReloadingDelegate(ProcessDefinition pd);
        public event ProcessReloadingDelegate ProcessReload;




        #region IProcessPackageStore Members


        public IList<string> ProcessDefinitionNames
        {
            get { throw new NotImplementedException(); }
        }

        #endregion

        #region IProcessPackageStore Members


        public string GetPackageContentPath(string contentFileName)
        {
            return Path.Combine(BaseDirectory, contentFileName);
        }

        #endregion
    }
}
